package com.shop.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.shop.feign.ProductFeign;
import com.shop.feign.SeckillCouponFeign;
import com.shop.pojo.pms.Pms_Sku_Info;
import com.shop.pojo.sms.Sms_Seckill_Sku_Relation;
import com.shop.service.SeckillService;
import com.shop.to.SeckillRedisTo;
import com.shop.to.SeckillSkuRelationAndSessionTo;
import com.shop.utils.R;
import com.shop.utils.RedisUtil;
import com.shop.utils.UUID;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
@Service
public class SeckillServiceImpl implements SeckillService {
    @Autowired
    private SeckillCouponFeign seckillCouponFeign;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ProductFeign productFeign;

    private final String SESSIONS_CACHE_PREFIX="seckill:sessions:";
    private final String SKUKILL_CACHE_PREFIX="seckill:skus:";
    private final String SKU_STOCK_SEMAPHORE="seckill:stock:";//+商品随机码
    @Override
    public void uploadSeckillSkuLatest3Ddays() {
        //扫描需要参与秒杀的活动
        R allSeckillSessionBySku = seckillCouponFeign.findAllSeckillSessionBySku();
        System.out.println(allSeckillSessionBySku);
        if(allSeckillSessionBySku.getCode()==0){
            //代表有上架商品
            List<SeckillSkuRelationAndSessionTo> sessionData = allSeckillSessionBySku.getData(new TypeReference<List<SeckillSkuRelationAndSessionTo>>() {
            });
            log.info(sessionData.toString());
            //缓存到redis
            //1、缓存活动信息
            saveSessionInfos(sessionData);
            //2、缓存活动的关联sku商品信息
            saveSessionSkuInfo(sessionData);
        }

    }
    /*
     * @Description 返回当前可以参与秒杀的商品信息
     * @param
     * @return java.util.List<com.shop.to.SeckillRedisTo>
     * @author liusupeng
     * @date 2020/11/17 13:35
     */
    @Override
    public List<SeckillRedisTo> getCurrentSeckillSkus() {
//1、确定当前时间属于哪个场次
        long time = new Date().getTime();

        Set<String> keys = redisUtil.findKeys(SESSIONS_CACHE_PREFIX);
        System.out.println("我是总数量："+keys.size());
        List<SeckillRedisTo> collect=new ArrayList<>();
        for (String key:keys){
            System.out.println("我是key"+key);
            String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
            String[] s = replace.split("_");
            Long start=Long.parseLong(s[0]);
            Long end=Long.parseLong(s[1]);
            if(time>=start&&time<=end){
                log.info("是可以有的");
                //2、获取这个秒杀场次需要的所有商品信息
                List<Object> range = redisUtil.lGet(key, 0, -1);//代表全部值

                System.out.println("我是集合啊"+range);
                System.out.println(range.size()+"shuzi");
                if(range.size()>0){
                    for (Object item : range) {
                        //这里需要进行一次转换，因为在redis里存的值是list集合，所以我们需要遍历下这个集合
                        List<Object> objects=new ArrayList<Object>((List<Object>) item);
                        //根据集合去读取里面的值，最终返回list
                        List<Object> objects1 = redisUtil.hgetList(SKUKILL_CACHE_PREFIX, objects);
                        //进行刚才返回的list进行转换，转换成我们需要的数据
                        collect = objects1.stream().map(seckillItem -> {
                            SeckillRedisTo seckillRedisTo = new SeckillRedisTo();
                            BeanUtils.copyProperties(seckillItem, seckillRedisTo);
                            return seckillRedisTo;
                        }).collect(Collectors.toList());
                    }
                }

                break;//我们只需要读取到我们当前场次的信息然后跳出即可
            }
        }


        return collect;
    }
    /*
     * @Description 根据skuid来查询当前秒杀信息
     * @param skuId
     * @return com.shop.to.SeckillRedisTo
     * @author liusupeng
     * @date 2020/11/26 9:25
     */
    @Override
    public SeckillRedisTo getSeckillRedisBySkuId(Long skuId) {

        BoundHashOperations<String, String, Object> hashList = redisUtil.getHashList(SKUKILL_CACHE_PREFIX);
        Set<String> keys = hashList.keys();
        if(keys.size()>0&&keys!=null){

               String regx="\\d_"+skuId;
               for (String key:keys){
                      if(Pattern.matches(regx,key)){

         SeckillRedisTo seckillRedisTo= (SeckillRedisTo) hashList.get(key);
         //处理随机码
                          long currTime = new Date().getTime();

                          if (currTime >= seckillRedisTo.getStartTime() && currTime <= seckillRedisTo.getEndTime()) {

                          } else {
                              // 不是秒杀时间，把随机码置空
                              seckillRedisTo.setRandomCode(null);
                          }

                          return seckillRedisTo;
                      }
               }
          }

        return null;
    }

    /*
     * @Description 缓存所有活动信息
     * @param seckillSkuRelationAndSessionTo
     * @return void
     * @author liusupeng
     * @date 2020/11/16 11:23
     */
    private void saveSessionInfos(List<SeckillSkuRelationAndSessionTo> sessions){
        sessions.stream().forEach(session->{
            Long start_time = session.getStart_time().getTime();
            Long end_time = session.getEnd_time().getTime();
            String key=SESSIONS_CACHE_PREFIX+start_time+"_"+end_time;

            boolean b = redisUtil.hasKey(key);
            if(b){
                log.info("当前"+key+"已经存在");
            }else{
                List<String> sessionId = session.getSmsSeckillSkuRelations().stream().map(item->{
                    return item.getPromotion_session_id()+"_"+item.getSku_id();
                }).collect(Collectors.toList());
                log.info(key+"::::"+sessionId);
                redisUtil.lSet(key,sessionId);
            }


        });


    }
    /*
     * @Description 缓存所有活动下关联的sku信息
     * @param seckillSkuRelationAndSessionTo
     * @return void
     * @author liusupeng
     * @date 2020/11/16 11:23
     */
    private void saveSessionSkuInfo(List<SeckillSkuRelationAndSessionTo> sessions){
        String uuid  =UUID.createCheckUUID().replace("-","");//随机码
        Map<String,Object> hashmap=new HashMap<>();
        sessions.stream().forEach(session->{

            session.getSmsSeckillSkuRelations().stream().forEach(Item->{

                boolean test=redisUtil.hHasKey(SKUKILL_CACHE_PREFIX,Item.getPromotion_session_id()+"_"+Item.getSku_id());
                //判断哈希里面的键里的key是否存在，如果存在则不进去，不存在则进去
                if(!redisUtil.hHasKey(SKUKILL_CACHE_PREFIX,Item.getPromotion_session_id()+"_"+Item.getSku_id())){
                    log.info(Item.getPromotion_session_id()+"_"+Item.getSku_id()+"已经存在");
                    //缓存商品
                    SeckillRedisTo seckillRedisTo=new SeckillRedisTo();
                    //1、sku的基本数据
                    R skuInfoBySkuId = productFeign.findSkuInfoBySkuId(Item.getSku_id());
                    if(skuInfoBySkuId.getCode()==0){
                        Pms_Sku_Info data = skuInfoBySkuId.getData(new TypeReference<Pms_Sku_Info>() {
                        });
                        seckillRedisTo.setSkuInfoVo(data);
                    }

                    System.out.println("------:"+Item);
                    //2、sku的秒杀信息
                    BeanUtils.copyProperties(Item,seckillRedisTo);
                    //3、设置上当前商品的秒杀时间
                    seckillRedisTo.setStartTime(session.getStart_time().getTime());
                    seckillRedisTo.setEndTime(session.getEnd_time().getTime());


                    /***
                     *  //4、随机码
                     *  // seckill?skuId=1  ：这个可能是我们访问秒杀时候的一个接口，如果被别人知道了，
                     *  但是我们的秒杀还没开始，但是他也是可能访问到的，因为我们的这个skuid=1肯定是存在的
                     *  所以我们要加上一个随机码，这个就说明就算我们知道这个接口也没什么用，我们这个随机码
                     *  只有在秒杀真正开始的时候才会暴露出来。
                     * */
                    seckillRedisTo.setRandomCode(uuid);
                    redisUtil.hset(SKUKILL_CACHE_PREFIX,Item.getPromotion_session_id()+"_"+Item.getSku_id(),seckillRedisTo);
                    //如果当前这个场次的商品的库存信息已经上架就不需要上架了

                    //5、使用库存作为引入分布式信号量
                    RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + uuid);

                    //商品可以秒杀的数量作为信号量,这样的话每次我们秒杀的数量减少一个他的信号量就减少一个
                    semaphore.trySetPermits(Item.getSeckill_count());
                    log.info("当前数量："+Item.getSeckill_count());
                }


            });
        });


    }
}
